package SGLisp

import (
	"fmt"

	. "git.oschina.net/yangdao/SGLisp/parse"
	. "git.oschina.net/yangdao/extlib/data_type"
)

var MainContext *Context

type Context struct {
	symbols map[string]Node
	parent  *Context
}

func LoadAstTree(fileName string) *Tree {
	t, err := File(fileName, false)
	if err != nil {
		fmt.Println(err)
	}
	return t
}
func (this *Context) GetSymblos() map[string]Node {
	return this.symbols
}
func NewContext(parent *Context) *Context {
	return &Context{
		make(map[string]Node),
		parent,
	}
}
func (c *Context) Import(other *Context, prefix string) {
	for key, value := range other.symbols {
		c.symbols[prefix+key] = value
	}
}

func (c *Context) ImportChildren(other *Context) {
	if other != nil {
		other.parent = c
	}
}

func (c *Context) Parent() *Context {
	return c.parent
}

func (this *Context) SetParent(ctx *Context) {
	this.parent = ctx
}

func (c *Context) Define(symbolName string, node Node) {
	c.symbols[symbolName] = node
}
func (this *Context) DefineFunc(symbolName string, fn func(args []Node, context *Context) Node) {
	this.symbols[symbolName] = &NativeFunction{MyFunction: fn}
}

func (this *Context) DefineExtFunc(symbolName string, fn func(args []Node, context *Context) Node) {
	this.symbols[symbolName] = &NativeFunction{FunctionExt: fn}
}

func (c *Context) LookUp(symbolName string) Node {
	val, defined := c.symbols[symbolName]
	if defined {
		return val
	} else if c.parent != nil {
		return c.parent.LookUp(symbolName)
	} else {
		return nil
	}
}

func InitRuntime() {
	if MainContext == nil {
		MainContext = CreateMainContext()
		defineCoreFunctions()
	}
}

func CreateMainContext() *Context {
	context := NewContext(nil)
	context.Define("log", &NativeFunction{MyFunction: _log})
	return context
}

func EvaluateTree(tree *Tree, context *Context) {
	for i := 0; i < len(tree.Roots); i++ {
		Evaluate(tree.Roots[i], context)
	}
}

/*         list
symbol(log)         list
              symbol(+) number(1) number(1)
(log (+ 1 1))
*/
func Evaluate(node Node, context *Context) Node {
	switch node.(type) {
	case *ListNode:
		firstChild := node.Children()[0]
		switch firstChild.(type) {
		case *SymbolNode:
			symNode := firstChild.(*SymbolNode)
			symValue := context.LookUp(symNode.Val)
			if symValue != nil {
				switch symValue.(type) {
				case *NativeFunction:
					return symValue.(*NativeFunction).Exec(node.Children(), context)
				case *MapNode:
					mapVal := symValue.(*MapNode).Val
					curK := node.Children()[1].(Node)
					return mapVal[curK.HashCode()].(Node)
				}
			} else {
				fmt.Println("Use Not Define SymbolNode " + node.String() + " :" + symNode.Val + ":")
			}
		case *MapNode:
			mapVal := firstChild.(*MapNode).Val
			curK := node.Children()[1].(Node)
			return mapVal[curK.HashCode()].(Node)
		}
	case *SymbolNode:
		symNode := node.(*SymbolNode)
		return context.LookUp(symNode.Val)
	case *MapNode:
		newMapNode := &MapNode{Pos: node.Position()}
		newMapNode.Val = make(map[interface{}]interface{})
		for k, v := range node.(*MapNode).Val {
			newMapNode.Val[k] = Evaluate(v.(Node), context)
		}
		return newMapNode
	case *VectorNode:
		newVecNode := &VectorNode{}
		curNodes := node.(*VectorNode).Nodes
		for i := 0; i < len(curNodes); i++ {
			newVecNode.Nodes = append(newVecNode.Nodes, Evaluate(curNodes[i], context))
		}
		return newVecNode
	default:
		return node
	}
	return nil
}

func EvalNode(node Node, ctx *Context) interface{} {
	switch node.(type) {
	case *ListNode:
		listNode := node.(*ListNode)
		retList := List()
		for i := 0; i < len(listNode.Children()); i++ {
			curNode := listNode.Children()[i]
			retList.Conj(EvalNode(curNode, ctx))
		}
		return retList
	case *MapNode:
		retMap := HashMap()
		for k, v := range node.(*MapNode).Val {
			if v != nil {
				retMap.Assoc(k, EvalNode(v.(Node), ctx))
			} else {
				retMap.Assoc(k, nil)
			}
		}
		return retMap
	case *BoolNode:
		return node.(*BoolNode).Val
	case *QuoteNode:
		return node
	case *StringNode:
		return node.(*StringNode).Val
	case *SymbolNode:
		return EvalNode(ctx.LookUp(node.(*SymbolNode).Val), ctx)
	case *ObjectNode:
		return node.(*ObjectNode).Val
	case *NilNode:
		return nil
	case *CharacterNode:
		return node.(*CharacterNode).Val
		//case *CommentNode:
		//	return node.(*CommentNode).Val
	case *KeywordNode:
		return node.(*KeywordNode).Val
	case *NumberNode:
		return node.(*NumberNode).Val
	case *VectorNode:
		vecNode := node.(*VectorNode)
		retVec := Vector()
		for i := 0; i < len(vecNode.Children()); i++ {
			retVec.Conj(EvalNode(vecNode.Children()[i], ctx))
		}
		return retVec
	case *FunctionNode:
		return node
	}
	return nil
}
